/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package fr.loof.fonzie.keywords;


import java.util.List;
import java.util.ListIterator;

import org.springframework.util.Assert;

import fr.loof.fonzie.parsers.Constraint;
import fr.loof.fonzie.parsers.JPQLPersistenceQuery;
import fr.loof.fonzie.parsers.KeyWord;
import fr.loof.fonzie.parsers.Property;
import fr.loof.fonzie.parsers.QueryElement;

public abstract class AbstractConstraint
    extends KeyWord
    implements Constraint
{
    protected abstract String getOperator();
    
    @SuppressWarnings( "unchecked" )
    public final void process( JPQLPersistenceQuery query, List<Class> params )
    {
        query.append( " " ).append( getOperator() );
        declareParameters( query, params );
    }

    public Property getProperty( ListIterator<QueryElement> elements )
    {
        Property target = null;
        int count = 0;
        while ( elements.hasPrevious() )
        {
            count++;
            QueryElement element = elements.previous();
            if ( element instanceof Property )
            {
                target = (Property) element;
                break;
            }
        }
        Assert.notNull( target, "No property in query for operator " + getKeyword() );
        while ( count-- > 0 )
        {
            elements.next();
        }
        return target;
    }

    @SuppressWarnings( "unchecked" )
    protected void declareParameters( JPQLPersistenceQuery query, List<Class> params )
    {
        declareParameter( query, params );
    }

    @SuppressWarnings( "unchecked" )
    protected void declareParameter( JPQLPersistenceQuery query, List<Class> params )
    {
        Property target = query.getCurrentProperty();
        query.append( " " ).append( getParameterExpression( query ) );
        query.add( target.getType() );

        Class arg = params.remove( 0 );
        doCheckType( arg, target );
    }

    protected String getParameterExpression( JPQLPersistenceQuery query )
    {
        return "?" + String.valueOf( query.parameters() + 1 );
    }

    @SuppressWarnings( "unchecked" )
    protected void doCheckType( Class arg, Property target )
    {
        Assert.isAssignable( target.getType(), arg, 
            "parameter of type " + arg + " is not compatible with property " + target );
    }


}